---
category: Level 2 — Intermediate
created: '2023-09-20'
description: Indent content in a text area using the Tab key with JavaScript DOM
openGraphCover: /og/html-dom/indent-content-text-area.png
title: Indent content in a text area using the Tab key
updated: '2023-10-03'
---

Adding indentation to a text area can make a big difference in real-life scenarios. For example, programmers write code snippets or scripts within a text area and use the tab key to indent different parts of their code, making it easier to read and understand.

Similarly, students may need to submit essays or research papers online and use text areas for submissions. Indenting paragraphs or bullet-pointed lists within these text areas can help students better organize their thoughts and ideas.

In this post, we'll learn how to insert indentation by pressing the Tab key.

## Adding indentation

To add indentation, simply add an event listener to the text area and detect when the `Tab` key is pressed. Then, insert a tab character (`\t`) into the text area at the current cursor position. This way, you can easily add indentation without having to reach for the space bar multiple times.

```js
const textarea = document.querySelector("textarea");

const insertTabCharacter = () => {
    const { value, selectionStart, selectionEnd } = textarea;

    // Insert tab character
    textarea.value = `${value.substring(0, selectionEnd)}\t${value.substring(selectionEnd)}`;

    // Move cursor to new position
    textarea.selectionStart = textarea.selectionEnd = selectionEnd + 1;
};

textarea.addEventListener('keydown', (e) => {
    if (e.key === 'Tab') {
        e.preventDefault();
        insertTabCharacter();
    }
});
```

Once we insert a tab character, we want the user to keep typing without any interruption. To achieve this, we move the cursor to the new position by setting both the `selectionStart` and `selectionEnd` properties of the text area to `selectionEnd + 1`. This ensures that the cursor is placed immediately after the inserted tab character.

Want to see it in action? Check out the demo below.

<Playground>
```css demo.css hidden
textarea {
    border: 1px solid rgb(203 213 225);
    border-radius: 0.5rem;
    padding: 0.5rem;
    width: 100%;
    height: 24rem;
}
```

```html index.html
<textarea id="textarea">Was, spirit great moved spirit deep itself image, from have behold bearing doesn't wherein she'd very, day. Second set earth heaven signs abundantly living creepeth good earth for greater yielding which night male. Bring midst whales blessed, is. From subdue. Yielding. Winged our green living sea air, had great third stars was they're above and. Morning light make first and kind sixth they're fowl, there. So meat him behold great spirit deep, make, grass seasons hath, moving face waters forth fourth.

Deep unto lights that. His. Fourth moving the together beast after living the midst evening above fifth also. Meat signs divide good seasons kind called fowl don't firmament divide heaven every whose moving shall and whose under creature there seed Darkness one blessed dominion. Own have forth she'd morning behold. In. Divided one you'll subdue whose made good. Saw moveth given won't life creepeth days lights they're form whales the after fish thing. And moveth. And that creepeth form you'll wherein morning saying moving fruitful. Herb set green behold had also bring Place land one second great saying. First god above called, can't subdue isn't years. Was called midst was. Image. Form. Kind waters. Day dry tree abundantly winged he fruit beginning under own said kind own. Face, and you first. Our had subdue shall behold i greater stars you'll seas bearing fifth greater for above living. Whose had.</textarea>
```

```js scripts.js hidden
document.addEventListener('DOMContentLoaded', () => {
    const textarea = document.getElementById('textarea');

    const insertTabCharacter = () => {
        const { value, selectionStart, selectionEnd } = textarea;

        // Insert tab character
        textarea.value = `${value.substring(0, selectionEnd)}\t${value.substring(selectionEnd)}`;

        // Move cursor to new position
        textarea.selectionStart = textarea.selectionEnd = selectionEnd + 1;
    };

    textarea.addEventListener('keydown', (e) => {
        if (e.key === 'Tab') {
            e.preventDefault();
            insertTabCharacter();
        }
    });
});
```
</Playground>

## Adding indentation to each line of selected text

When implementing a solution that involves using the `Tab` key for indenting text areas, it's important to consider a potential issue. If a user has selected some text within the text area and then presses the `Tab` key, the selected text will be replaced with a tab character instead of being indented.

To avoid this problem, we can check if there is any text currently selected in the text area before inserting the tab character. We can achieve this by comparing the `selectionStart` and `selectionEnd` properties. If they are equal, then no text is currently selected. If they are different, then there is some text selected within the text area.

```js
(selectionStart === selectionEnd)
    // Insert tab character
    ? insertTabCharacter()
    // Add indentation to each line of selected text
    : addIndentation();
```

In our updated code snippet, we first check if there is any selected text before deciding whether to insert a tab character or add indentation to each line of the selected text. This ensures that we don't accidentally replace any selected text with a tab character.

Here's the updated code to take care of the issue:

```js
const textarea = document.querySelector('textarea');

// Add indentation to each line of selected text
const addIndentation = () => {
    // ...
}

textarea.addEventListener('keydown', (e) => {
    if (e.key === 'Tab') {
        e.preventDefault();
        const { selectionStart, selectionEnd } = textarea;
        (selectionStart === selectionEnd)
            ? insertTabCharacter()
            : addIndentation();
    }
});
```

In the `keydown` event handler example, the function checks the starting and ending positions of the selection. If the positions are different, it means that you have selected some text in the text area. In such cases, the `addIndentation` function is used to add indentation to each line of the selected text.

First, we first need to split the text into lines before and after the selection.

```js
const addIndentation = () => {
    const { value, selectionStart, selectionEnd } = textarea;
    const linesBeforeCaret = value.substring(0, selectionStart).split('\n');
    const startLine = linesBeforeCaret.length - 1;
    const endLine = value.substring(0, selectionEnd).split('\n').length - 1;
};
```

Then, we calculate which lines are within the selection. For each selected line, we add a tab character at the beginning using template literals. Finally, we reconstruct the entire string by joining all lines with newline characters and set this new value as the value of the textarea element.

```js
const newValue = value
    .split('\n')
    .map((line, i) => (i >= startLine && i <= endLine) ? `\t${line}` : line)
    .join('\n');
textarea.value = newValue;
```

To ensure that the cursor stays in place relative to the newly added indentation, we need to adjust both `selectionStart` and `selectionEnd`. First, we calculate `newSelectionStart` by checking if the starting line of the selection contains any non-whitespace characters. If it does, then we add 1 to `selectionStart`, which moves the cursor one position to the right. Otherwise, `newSelectionStart` remains equal to `selectionStart`.

```js
const startLineText = linesBeforeCaret[startLine];
const newSelectionStart = startLineText && /\S/.test(startLineText)
                        ? selectionStart + 1
                        : selectionStart;
```

Next, we calculate `newSelectionEnd` by adding to `selectionEnd` the number of lines that were modified by inserting a tab character at the beginning of each selected line. Specifically, `(endLine - startLine + 1)` gives us the number of lines in the selection that were modified.

```js
const newSelectionEnd = selectionEnd + (endLine - startLine + 1);
```

By adjusting both `selectionStart` and `selectionEnd`, we ensure that the cursor stays in its original position relative to the newly indented text.

Now you can safely use the `Tab` key to add indentation to your text areas without worrying about accidentally replacing selected text.

Take a look at the final demo below:

<Playground>
```css demo.css hidden
textarea {
    border: 1px solid rgb(203 213 225);
    border-radius: 0.5rem;
    padding: 0.5rem;
    width: 100%;
    height: 24rem;
}
```

```html index.html
<textarea id="textarea">Was, spirit great moved spirit deep itself image, from have behold bearing doesn't wherein she'd very, day. Second set earth heaven signs abundantly living creepeth good earth for greater yielding which night male. Bring midst whales blessed, is. From subdue. Yielding. Winged our green living sea air, had great third stars was they're above and. Morning light make first and kind sixth they're fowl, there. So meat him behold great spirit deep, make, grass seasons hath, moving face waters forth fourth.

Deep unto lights that. His. Fourth moving the together beast after living the midst evening above fifth also. Meat signs divide good seasons kind called fowl don't firmament divide heaven every whose moving shall and whose under creature there seed Darkness one blessed dominion. Own have forth she'd morning behold. In. Divided one you'll subdue whose made good. Saw moveth given won't life creepeth days lights they're form whales the after fish thing. And moveth. And that creepeth form you'll wherein morning saying moving fruitful. Herb set green behold had also bring Place land one second great saying. First god above called, can't subdue isn't years. Was called midst was. Image. Form. Kind waters. Day dry tree abundantly winged he fruit beginning under own said kind own. Face, and you first. Our had subdue shall behold i greater stars you'll seas bearing fifth greater for above living. Whose had.</textarea>
```

```js scripts.js
document.addEventListener('DOMContentLoaded', () => {
    const textarea = document.getElementById('textarea');

    // Insert tab character
    const insertTabCharacter = () => {
        const { value, selectionStart, selectionEnd } = textarea;

        // Insert tab character
        textarea.value = `${value.substring(0, selectionEnd)}\t${value.substring(selectionEnd)}`;

        // Move cursor to new position
        textarea.selectionStart = textarea.selectionEnd = selectionEnd + 1;
    };

    // Add indentation to each line of selected text
    const addIndentation = () => {
        const { value, selectionStart, selectionEnd } = textarea;

        const linesBeforeCaret = value.substring(0, selectionStart).split('\n');
        const startLine = linesBeforeCaret.length - 1;
        const endLine = value.substring(0, selectionEnd).split('\n').length - 1;

        const newValue = value
            .split('\n')
            .map((line, i) => (i >= startLine && i <= endLine) ? `\t${line}` : line)
            .join('\n');

        textarea.value = newValue;

        const startLineText = linesBeforeCaret[startLine];
        textarea.selectionStart = startLineText && /\S/.test(startLineText)
                                ? selectionStart + 1
                                : selectionStart;
        textarea.selectionEnd = selectionEnd + (endLine - startLine + 1);
    };

    textarea.addEventListener('keydown', (e) => {
        if (e.key === 'Tab') {
            e.preventDefault();
            const { selectionStart, selectionEnd } = textarea;
            (selectionStart === selectionEnd)
                ? insertTabCharacter()
                : addIndentation();
        }
    });
});
```
</Playground>

## Conclusion

With this code implementation, users can now use the `tab` key to effortlessly indent their text within the text area. This seemingly small change can vastly improve the readability and organization of user-submitted content.

## See also

-   [Build a simple code editor](https://phuoc.ng/collection/mirror-a-text-area/build-a-simple-code-editor/)
-   [Remove indentation in a text area using the Shift+Tab key combination](https://phuoc.ng/collection/html-dom/remove-indentation-in-a-text-area-using-the-shift-tab-key-combination/)
